package com.sleep.ws.server;


import com.sleep.ws.server.handler.BaseWebSocketHandler;
import com.sleep.ws.server.handler.BinaryWebSocketFrameHandler;
import com.sleep.ws.server.handler.MonitorHandler;
import com.sleep.ws.server.utils.SslUtil;
import io.netty.channel.*;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.util.CharsetUtil;
import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.util.StringUtils;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import java.util.concurrent.TimeUnit;

/**
 * 服务器通道初始化
 *
 */

public class WsServerChannelInitializer extends ChannelInitializer<SocketChannel> {

    private static final Logger log = LoggerFactory.getLogger(WsServerChannelInitializer.class);

    private String keyStore;
    private String password;
    @Setter
    private String path;

    @Override
    protected void initChannel(SocketChannel socketChannel) {
        ChannelPipeline pipeline = socketChannel.pipeline();
        if(StringUtils.hasLength(keyStore)){
            pipeline.addLast(new SslHandler(initSsl()));
        }
        pipeline.addLast(
                new HttpContentCompressor(),
                new IdleStateHandler(10, 5, 0, TimeUnit.SECONDS),
                new LoggingHandler(LogLevel.TRACE),
                new HttpServerCodec(),
                // 设置最大聚合字节长度
                new HttpObjectAggregator(1024 * 1024),
                new StringDecoder(CharsetUtil.UTF_8),
                new StringEncoder(CharsetUtil.UTF_8),
                new ChunkedWriteHandler(),
//                new HttpObjectAggregator(65536),
                new ChannelInboundHandlerAdapter() {
                    @Override
                    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                        if(msg instanceof FullHttpRequest) {
                            FullHttpRequest fullHttpRequest = (FullHttpRequest) msg;
                            String uri = fullHttpRequest.uri();
                            if (!("/"+path).equals(uri)) {
                                // 访问的路径不是 websocket的端点地址，响应404
                                ctx.channel().writeAndFlush(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_FOUND))
                                        .addListener(ChannelFutureListener.CLOSE);
                                return ;
                            }
                        }
                        super.channelRead(ctx, msg);
                    }
                },
                new MonitorHandler(),
                new WebSocketServerCompressionHandler(),
                new WebSocketServerProtocolHandler("/"+path, null, true, 1024 * 1024),
                new BaseWebSocketHandler<>(),
                new BinaryWebSocketFrameHandler(),
                new ReadTimeoutHandler(60 * 60 * 24));
    }

    private SSLEngine initSsl() {
        SSLContext sslContext = null;
        try {
            sslContext = SslUtil.createSSLContext("JKS", new DefaultResourceLoader().getResource(keyStore).getInputStream(), password);
            SSLEngine sslEngine = sslContext.createSSLEngine();
            sslEngine.setUseClientMode(false);
            return sslEngine;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public void setSslKeyStore(String keyStore){
        this.keyStore = keyStore;
    }
    public void setSslPassWord(String password){
        this.password = password;
    }

}
